﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Xml;
using System.Runtime.Serialization.Formatters.Binary;

namespace XmlVisualizer.DiagramDesigner {

	/// <summary>
	/// Custom canvas class.
	/// </summary>
    public partial class DesignerCanvas : Canvas {
		#region Fields
		private Point? rubberbandSelectionStartPoint = null;
		#endregion

		#region Properties
		private SelectionService selectionService_;
		internal SelectionService selectionService {
			get {
				if (selectionService_ == null)
					selectionService_ = new SelectionService(this);

				return selectionService_;
			}
		}
		#endregion	

		#region Event handlers
		/// <summary>
		/// Invoked on MouseDown event.
		/// </summary>
		/// <param name="e">The event data.</param>
		protected override void OnMouseDown(MouseButtonEventArgs e) {
			base.OnMouseDown(e);
			if (e.Source == this) {
				// in case that this click is the start of a 
				// drag operation we cache the start point
				this.rubberbandSelectionStartPoint = new Point?(e.GetPosition(this));

				// if you click directly on the canvas all 
				// selected items are 'de-selected'
				selectionService.ClearSelection();
				Focus(); // causes scrollviewer to scroll if enabled
				e.Handled = true;
			}
		}

		/// <summary>
		/// Inovked on MouseMove event.
		/// </summary>
		/// <param name="e">The event data.</param>
		protected override void OnMouseMove(MouseEventArgs e) {
			base.OnMouseMove(e);

			// if mouse button is not pressed we have no drag operation, ...
			if (e.LeftButton != MouseButtonState.Pressed)
				this.rubberbandSelectionStartPoint = null;

			// ... but if mouse button is pressed and start
			// point value is set we do have one
			if (this.rubberbandSelectionStartPoint.HasValue) {
				// create rubberband adorner
				AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
				if (adornerLayer != null) {
					RubberbandAdorner adorner = new RubberbandAdorner(this, rubberbandSelectionStartPoint);
					if (adorner != null) {
						adornerLayer.Add(adorner);
					}
				}
			}
			e.Handled = true;
		}

		/// <summary>
		/// Invoked on Drop event.
		/// </summary>
		/// <param name="e">The event data.</param>
		protected override void OnDrop(DragEventArgs e) {
			base.OnDrop(e);
			DragObject dragObject = e.Data.GetData(typeof(DragObject)) as DragObject;
			if (dragObject != null && !String.IsNullOrEmpty(dragObject.Xaml)) {
				DesignerItem newItem = null;
				Object content = XamlReader.Load(XmlReader.Create(new StringReader(dragObject.Xaml)));

				if (content != null) {
					newItem = new DesignerItem();
					newItem.Content = content;

					Point position = e.GetPosition(this);

					if (dragObject.DesiredSize.HasValue) {
						Size desiredSize = dragObject.DesiredSize.Value;
						newItem.Width = desiredSize.Width;
						newItem.Height = desiredSize.Height;

						DesignerCanvas.SetLeft(newItem, Math.Max(0, position.X - newItem.Width / 2));
						DesignerCanvas.SetTop(newItem, Math.Max(0, position.Y - newItem.Height / 2));
					} else {
						DesignerCanvas.SetLeft(newItem, Math.Max(0, position.X));
						DesignerCanvas.SetTop(newItem, Math.Max(0, position.Y));
					}

					Canvas.SetZIndex(newItem, this.Children.Count);
					this.Children.Add(newItem);
					SetConnectorDecoratorTemplate(newItem);

					//update selection
					this.selectionService.SelectItem(newItem);
					newItem.Focus();
				}

				e.Handled = true;
			}
		}

		/// <summary>
		/// Invoked on Measure event.
		/// </summary>
		/// <param name="constraint"></param>
		/// <returns></returns>
		protected override Size MeasureOverride(Size constraint) {
			Size size = new Size();

			foreach (UIElement element in this.InternalChildren) {
				double left = Canvas.GetLeft(element);
				double top = Canvas.GetTop(element);
				left = double.IsNaN(left) ? 0 : left;
				top = double.IsNaN(top) ? 0 : top;

				//measure desired size for each child
				element.Measure(constraint);

				Size desiredSize = element.DesiredSize;
				if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height)) {
					size.Width = Math.Max(size.Width, left + desiredSize.Width);
					size.Height = Math.Max(size.Height, top + desiredSize.Height);
				}
			}
			// add margin
			size.Width += 10;
			size.Height += 10;
			return size;
		}
		#endregion

		#region Methods
		// changed to public, was private
		/// <summary>
		/// Sets connector decorator template.
		/// </summary>
		/// <param name="item">DesignerItem to apply the template to.</param>
		public void SetConnectorDecoratorTemplate(DesignerItem item) {
			if (item.ApplyTemplate() && item.Content is UIElement) {
				ControlTemplate template = DesignerItem.GetConnectorDecoratorTemplate(item.Content as UIElement);
				Control decorator = item.Template.FindName("PART_ConnectorDecorator", item) as Control;
				if (decorator != null && template != null)
					decorator.Template = template;
			}
		}
		#endregion
    }
}
